package com.rw.coco.util;

import com.rw.coco.handler.message.reply.handler.command.assistant.ra.SuccessLevel;
import com.rw.coco.handler.message.reply.handler.command.bean.CocoProp;
import lombok.Getter;

/**
 * 骰点规则
 */
@Getter
public enum DiceRule {
    /**
     * 标准规则:
     * <ul>大成功:
     *   <ul> [ 1D100 = 1 ] </ul>
     * </ul>
     * <ul>
     *   大失败:
     *     <ul> 属性值 < 50 [ 1D100 > 95 ] </ul>
     *     <ul> 属性值 >= 50 [ 1D100 = 100 ] </ul>
     * </ul>
     */
    STANDARD_RULE(0),

    /**
     * 平衡规则:
     * <ul>大成功:
     *     <ul> 属性值 < 50 [ 1D100 = 1 ] </ul>
     *     <ul> 属性值 >= 50 [ 1D100 <= 5 ] </ul>
     * </ul>
     * <ul>
     *   大失败:
     *     <ul> 属性值 < 50 [ 1D100 > 95 ] </ul>
     *     <ul> 属性值 >= 50 [ 1D100 = 100 ] </ul>
     * </ul>
     */
    BALANCE_RULE(1),

    /**
     * 固定规则:
     * <ul>大成功:
     *     <ul> [ 1D100 <= 5 ] 且检定成功 </ul>
     * </ul>
     * <ul>
     *   大失败:
     *     <ul> [ 1D100 <= 5 ] 且检定失败 </ul>
     * </ul>
     */
    FIX_RULE(2),

    /**
     * 简单固定规则:
     * <ul>大成功:
     *     <ul> [ 1D100 <= 5 ] </ul>
     * </ul>
     * <ul>
     *   大失败:
     *     <ul> [ 1D100 <= 5 ] </ul>
     * </ul>
     */
    SIMPLE_FIX_RULE(3),

    /**
     * 十分之一规则:
     * <ul>大成功:
     *     <ul> 属性值 >= 10 [ 1D100 = 1 ] </ul>
     *     <ul> 属性值 < 10 [ 无大成功 ] </ul>
     * </ul>
     * <ul>
     *   大失败:
     *     <ul> 属性值 < 50 [ 1D100 > 95 + 属性的十分之一 ] (向下取整) </ul>
     *     <ul> 属性值 >= 50 [ 1D100 = 100 ] </ul>
     * </ul>
     */
    ONE_TENTH_RULE(4),

    /**
     * 宽松十分之一规则:
     * <ul>大成功:
     *   <ul> 属性值 >= 10 [ 1D100 <= 2 ] </ul>
     *   <ul> 属性值 >= 5 [ 1D100 = 1 ] </ul>
     *   <ul> 属性值 < 5 [ 无大成功 ] </ul>
     * </ul>
     * <ul>
     *   大失败:
     *     <ul> 属性值 < 50 [ 1D100 > 95 ] </ul>
     *     <ul> 属性值 >= 50 [ 1D100 = 100 ] </ul>
     * </ul>
     */
    LENIENT_ONE_TENTH_RULE(5);

    // 序号
    private final int index;

    DiceRule(int index) {
        this.index = index;
    }

    /**
     * 获取对应序号代表的房规
     *
     * @param index 序号，限制为 0-5
     * @return 房规
     */
    public static DiceRule of(int index) {
        for (DiceRule diceRule : values()) {
            if (diceRule.index == index) {
                return diceRule;
            }
        }

        throw new IllegalArgumentException("The dice rule at index " + index + " cannot be found");
    }

    /**
     * 根据不同的规则判定成功等级
     *
     * @param targetNum 目标属性值
     * @param randomNum 随机出的随机数字
     * @return 成功等级
     */
    public SuccessLevel roll(int targetNum, int randomNum) {
        // 判断数值合理性
        assertNum(targetNum, randomNum);

        // 根据不同的规则执行不同的判定
        switch (this) {
            case STANDARD_RULE:
                return standardRule(targetNum, randomNum);
            case BALANCE_RULE:
                return balanceRule(targetNum, randomNum);
            case FIX_RULE:
                return fixRule(targetNum, randomNum);
            case SIMPLE_FIX_RULE:
                return simpleFixRule(targetNum, randomNum);
            case ONE_TENTH_RULE:
                return oneTenthRule(targetNum, randomNum);
            case LENIENT_ONE_TENTH_RULE:
                return lenientOneTenthRule(targetNum, randomNum);
        }

        throw new IllegalArgumentException("Unknown dice rule!!");
    }

    /**
     * 标准规则计算
     *
     * @param targetNum 目标值
     * @param randomNum 随机值
     * @return 成功等级
     */
    private SuccessLevel standardRule(int targetNum, int randomNum) {
        // 如果随机数等于1，则判定为大成功
        if (randomNum == 1) {
            return SuccessLevel.CRITICAL_SUCCESS;
        }

        // 低属性值的大失败条件
        boolean smallTargetNumFatal = targetNum < 50 && randomNum > 95;

        // 高属性值的大失败条件
        boolean bigTargetNumFatal = targetNum >= 50 && randomNum == 100;

        // 大失败判定
        if (smallTargetNumFatal || bigTargetNumFatal) {
            return SuccessLevel.FATAL;
        }

        // 正常判定
        return ordinaryRule(targetNum, randomNum);
    }

    /**
     * 平衡规则计算
     *
     * @param targetNum 目标值
     * @param randomNum 随机值
     * @return 成功等级
     */
    private SuccessLevel balanceRule(int targetNum, int randomNum) {
        // 如果判定失败，则结果和 标准规则 是一样的
        if (randomNum > targetNum) {
            return standardRule(targetNum, randomNum);
        }

        // 低属性值的大成功条件
        boolean smallTargetCriticalSuccess = targetNum < 50 && randomNum == 1;

        // 高属性值的大成功条件
        boolean bigTargetCriticalSuccess = targetNum >= 50 && randomNum <= 5;

        // 大成功判定
        if (smallTargetCriticalSuccess || bigTargetCriticalSuccess) {
            return SuccessLevel.CRITICAL_SUCCESS;
        }

        // 正常判定
        return ordinaryRule(targetNum, randomNum);
    }

    /**
     * 固定规则
     *
     * @param targetNum 目标值
     * @param randomNum 随机值
     * @return 成功等级
     */
    private SuccessLevel fixRule(int targetNum, int randomNum) {
        // 如果判定成功，则结果和 简单固定规则 是一样的
        if (randomNum <= targetNum) {
            return simpleFixRule(targetNum, randomNum);
        }

        // 正常判定
        return ordinaryRule(targetNum, randomNum);
    }

    /**
     * 简单固定规则计算
     *
     * @param targetNum 目标值
     * @param randomNum 随机值
     * @return 成功等级
     */
    private SuccessLevel simpleFixRule(int targetNum, int randomNum) {
        // 大成功
        if (randomNum <= 5) {
            return SuccessLevel.CRITICAL_SUCCESS;
        }

        // 大失败
        if (randomNum > 95) {
            return SuccessLevel.FATAL;
        }

        // 正常判定
        return ordinaryRule(targetNum, randomNum);
    }

    /**
     * 十分之一规则计算
     *
     * @param targetNum 目标值
     * @param randomNum 随机值
     * @return 成功等级
     */
    private SuccessLevel oneTenthRule(int targetNum, int randomNum) {
        // 大成功判定
        if (targetNum >= 10 && randomNum == 1) {
            return SuccessLevel.CRITICAL_SUCCESS;
        }

        // 属性值的十分之一
        int oneTenthTargetNum = targetNum / 10;

        // 低属性值的大失败条件
        boolean smallTargetNumFatal = targetNum < 50 && randomNum > 95 + oneTenthTargetNum;

        // 高属性值的大失败条件
        boolean bigTargetNumFatal = targetNum >= 50 && randomNum == 100;

        // 大失败判定
        if (smallTargetNumFatal || bigTargetNumFatal) {
            return SuccessLevel.FATAL;
        }

        // 正常判定
        return ordinaryRule(targetNum, randomNum);
    }

    /**
     * 宽松十分之一规则计算
     *
     * @param targetNum 目标值
     * @param randomNum 随机值
     * @return 成功等级
     */
    private SuccessLevel lenientOneTenthRule(int targetNum, int randomNum) {
        // 如果判定失败，则结果和 标准规则 是一样的
        if (randomNum > targetNum) {
            return standardRule(targetNum, randomNum);
        }

        // 低属性值的大成功条件
        boolean smallTargetCriticalSuccess = targetNum >= 10 && randomNum <= 2;

        // 高属性值的大成功条件
        boolean bigTargetCriticalSuccess = targetNum >= 5 && randomNum == 1;

        // 大成功判定
        if (smallTargetCriticalSuccess || bigTargetCriticalSuccess) {
            return SuccessLevel.CRITICAL_SUCCESS;
        }

        // 正常判定
        return ordinaryRule(targetNum, randomNum);
    }

    /**
     * 既没有大成功，也没有大失败的，平平无奇的规则
     *
     * @param targetNum 目标值
     * @param randomNum 随机值
     * @return 成功等级
     */
    private SuccessLevel ordinaryRule(int targetNum, int randomNum) {
        // 极难成功
        if (randomNum <= targetNum / 5) {
            return SuccessLevel.EXTREME_SUCCESS;
        }

        // 困难成功
        if (randomNum <= targetNum / 2) {
            return SuccessLevel.HARD_SUCCESS;
        }

        // 普通成功
        if (randomNum <= targetNum) {
            return SuccessLevel.REGULAR_SUCCESS;
        }

        // 普通成功
        return SuccessLevel.FAILURE;
    }

    /**
     * 判断数值是否在接受范围以内
     *
     * @param targetNum 目标值
     * @param randomNum 随机值
     */
    private void assertNum(int targetNum, int randomNum) {
        if (randomNum <= 0 || randomNum > 100) {
            throw new IllegalArgumentException("random number can only in (0, 100]! random number:" + randomNum);
        }

        if (targetNum <= 0 || targetNum > CocoProp.MAX_VALUE) {
            throw new IllegalArgumentException("target number can only in (0, " + CocoProp.MAX_VALUE +
                    "]! target number:" + targetNum);
        }
    }
}
